﻿using PacketDotNet;
using SharpPcap;

namespace WebMiddleware.Utils
{
    /// <summary>
    /// TCP 数据流监听器。
    /// </summary>
    internal class TcpStreamListenner : IDisposable
    {
        private readonly StreamWriter _log;

        public TcpStreamListenner()
        {
            var filePath =  Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt");
            var file = File.Open(filePath, FileMode.OpenOrCreate);
            _log = new StreamWriter(file);
        }

        private ILiveDevice? _currentDevice;

        public void Open(string filter, string description, int timeOut = 1000)
        {
            if (_currentDevice != null)
            {
                return;
            }

            var devices = CaptureDeviceList.Instance;

            _currentDevice = devices?.FirstOrDefault(item => item.Description == description);

            if (_currentDevice == null)
            {
                throw new InvalidOperationException($"未找到{description}网卡设备！");
            }

            _currentDevice.OnPacketArrival -= OnPacketArrival;
            _currentDevice.OnPacketArrival += OnPacketArrival;

            try
            {
                _currentDevice.Open(DeviceModes.Promiscuous, timeOut);
                _currentDevice.Filter = filter;
                _currentDevice.StartCapture();
            }
            catch (Exception ex)
            {
                if (_currentDevice.Started)
                {
                    _currentDevice.StopCapture();
                }

                _currentDevice.Close();
                _currentDevice = null;
            }
        }

        private void OnPacketArrival(object sender, PacketCapture e)
        {
            var rowPacket = e.GetPacket();
            var packet = Packet.ParsePacket(rowPacket.LinkLayerType, rowPacket.Data);
            var tcpPacket = packet.Extract<TcpPacket>();

            if (tcpPacket != null)
            {
                var packetFlag = new List<string>();

                // 数据推送包
                if (tcpPacket.Push)
                {
                    packetFlag.Add("PSH");
                }

                // TCP 断开包
                if (tcpPacket.Finished)
                {
                    packetFlag.Add("FIN");
                }

                // TCP 握手包
                if (tcpPacket.Synchronize)
                {
                    packetFlag.Add("SYN");
                }

                // 应答包
                if (tcpPacket.Acknowledgment)
                {
                    packetFlag.Add("ACK");
                }

                if (packetFlag.Any())
                {
                    var title = "[ ";
                    packetFlag.ForEach(item =>
                    {
                        title += item + " ";
                    });
                    title += "]";

                    if (tcpPacket.PayloadData != null && tcpPacket.PayloadData.Length > 0)
                    {
                        var data = BitConverter.ToString(tcpPacket.PayloadData).Replace("-", " ");
                        var printValue = $"{tcpPacket.SourcePort}\t->\t{tcpPacket.DestinationPort}\t" + title + " " + data;
                        Console.WriteLine(DateTime.Now.ToString() + "\t" + printValue);
                        _log.WriteLine(DateTime.Now.ToString() + "\t" + printValue);
                    }
                    else
                    {
                        var printValue = $"{tcpPacket.SourcePort}\t->\t{tcpPacket.DestinationPort}\t" + title;
                        Console.WriteLine(DateTime.Now.ToString() + "\t" + printValue);
                        _log.WriteLine(DateTime.Now.ToString() + "\t" + printValue);
                    }
                }
            }
        }

        public void Close()
        {
            if (_currentDevice != null)
            {
                if (_currentDevice.Started)
                {
                    _currentDevice.StopCapture();
                }

                _currentDevice.Close();
            }
        }

        public void Dispose()
        {
            _log.Close();
            _log.Dispose();
        }
    }
}
